---
title: "Kastrax AI Agent Tools System | Agent Documentation | Kastrax"
description: Comprehensive guide to implementing and using tools with Kastrax AI Agents. Learn how to create custom tools, integrate with external systems, and enhance your agents' capabilities.
---

# Kastrax AI Agent Tools System ✅

Tools are a fundamental component of the Kastrax AI Agent framework that extend agents' capabilities beyond conversation. They allow agents to perform specific actions, access external systems, process data, and interact with the world. In Kastrax, tools are implemented as strongly-typed Kotlin functions with well-defined input/output schemas, execution logic, and error handling.

## Tool Architecture in Kastrax ✅

Kastrax implements a robust tool system with several key components:

1. **Tool Definition**: A strongly-typed interface that defines the tool's inputs, outputs, and metadata
2. **Tool Implementation**: The actual execution logic that performs the tool's function
3. **Tool Registry**: A central registry that makes tools available to agents
4. **Tool Invocation**: The mechanism by which agents call tools during execution

This architecture ensures type safety, proper error handling, and seamless integration with the agent system.

## Creating Basic Tools ✅

Let's create a simple weather information tool that demonstrates the basic structure of Kastrax tools:

```kotlin filename="src/main/kotlin/ai/kastrax/tools/WeatherTool.kt"
import ai.kastrax.core.tool.Tool
import ai.kastrax.core.tool.ToolDefinition
import ai.kastrax.core.tool.ToolResult
import kotlinx.serialization.Serializable

// Define the input parameters for the tool
@Serializable
data class WeatherToolInput(
    val city: String,
    val units: String = "metric" // Optional parameter with default value
)

// Define the output structure
@Serializable
data class WeatherToolOutput(
    val temperature: Double,
    val conditions: String,
    val humidity: Int,
    val windSpeed: Double
)

// Implement the tool
class WeatherTool : Tool<WeatherToolInput, WeatherToolOutput> {
    // Define tool metadata
    override val definition = ToolDefinition(
        name = "get_weather",
        description = "Fetches current weather information for a specified city",
        inputType = WeatherToolInput::class,
        outputType = WeatherToolOutput::class
    )

    // Implement the execution logic
    override suspend fun execute(input: WeatherToolInput): ToolResult<WeatherToolOutput> {
        return try {
            // In a real implementation, this would call a weather API
            val weatherData = fetchWeatherData(input.city, input.units)

            ToolResult.Success(
                WeatherToolOutput(
                    temperature = weatherData.temperature,
                    conditions = weatherData.conditions,
                    humidity = weatherData.humidity,
                    windSpeed = weatherData.windSpeed
                )
            )
        } catch (e: Exception) {
            ToolResult.Error("Failed to fetch weather data: ${e.message}")
        }
    }

    // Helper method to fetch weather data (would be implemented with actual API calls)
    private suspend fun fetchWeatherData(city: String, units: String): WeatherData {
        // This would be an actual API call in a real implementation
        // For demonstration, we're returning mock data
        return WeatherData(22.5, "Sunny", 65, 10.2)
    }

    // Internal data class for the API response
    private data class WeatherData(
        val temperature: Double,
        val conditions: String,
        val humidity: Int,
        val windSpeed: Double
    )
}
```

## Adding Tools to an Agent ✅

Once you've created your tools, you need to register them with your agent. In Kastrax, this is done by adding the tools to the agent's configuration:

```kotlin filename="src/main/kotlin/ai/kastrax/agents/WeatherAgent.kt"
import ai.kastrax.core.agent.Agent
import ai.kastrax.core.agent.AgentConfig
import ai.kastrax.core.llm.DeepSeekProvider
import ai.kastrax.tools.WeatherTool

class WeatherAgent {
    // Create an instance of the weather tool
    private val weatherTool = WeatherTool()

    // Create the agent with the tool
    val agent = Agent(
        config = AgentConfig(
            name = "WeatherAssistant",
            description = "An assistant that can provide weather information for cities around the world",
            instructions = """
                You are a helpful weather assistant that provides accurate weather information.
                When asked about the weather in a specific location, use the get_weather tool to fetch current data.
                Present the information in a clear, concise format including temperature, conditions, humidity, and wind speed.
                Always specify whether temperatures are in Celsius (metric) or Fahrenheit (imperial) based on the units used.
            """.trimIndent(),
            llmProvider = DeepSeekProvider(apiKey = "your-deepseek-api-key"),
            tools = listOf(weatherTool)
        )
    )
}
```

## Registering Tools in the Application ✅

In a larger application, you might want to register your tools centrally so they can be used by multiple agents:

```kotlin filename="src/main/kotlin/ai/kastrax/Application.kt"
import ai.kastrax.core.KastraxSystem
import ai.kastrax.tools.WeatherTool
import ai.kastrax.tools.CalculatorTool
import ai.kastrax.tools.SearchTool
import ai.kastrax.agents.WeatherAgent

class Application {
    // Initialize the Kastrax system
    private val system = KastraxSystem()

    // Register tools centrally
    init {
        // Register individual tools
        system.registerTool(WeatherTool())
        system.registerTool(CalculatorTool())
        system.registerTool(SearchTool())

        // Register agents
        system.registerAgent(WeatherAgent().agent)
    }

    fun start() {
        // Start the application
        system.start()
    }
}
```

This approach allows you to maintain a central registry of tools that can be shared across different agents in your application.

## Handling Cancellation ✅

Kastrax supports cancellation of tool execution through Kotlin's built-in cancellation mechanism. This allows you to gracefully handle situations where a tool execution needs to be stopped:

```kotlin
import ai.kastrax.core.tool.Tool
import ai.kastrax.core.tool.ToolResult
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import kotlinx.coroutines.Dispatchers

class LongRunningTool : Tool<LongRunningToolInput, LongRunningToolOutput> {
    // Tool definition omitted for brevity

    override suspend fun execute(input: LongRunningToolInput): ToolResult<LongRunningToolOutput> {
        return try {
            // Set a timeout for the operation
            withTimeout(30000) { // 30 seconds timeout
                withContext(Dispatchers.IO) {
                    // Check for cancellation periodically
                    kotlinx.coroutines.ensureActive()

                    // Perform long-running operation
                    val result = performLongRunningTask(input)

                    // Check again before returning
                    kotlinx.coroutines.ensureActive()

                    ToolResult.Success(result)
                }
            }
        } catch (e: CancellationException) {
            // Handle cancellation gracefully
            ToolResult.Error("Operation was cancelled")
        } catch (e: Exception) {
            ToolResult.Error("Operation failed: ${e.message}")
        }
    }
}
```

When using the agent, you can provide a cancellation handler:

```kotlin
val job = kotlinx.coroutines.launch {
    agent.generate(
        input = "Perform a long-running analysis",
        sessionId = "user-123",
        conversationId = "analysis-session"
    )
}

// Cancel the operation after some condition
if (userRequestedCancellation) {
    job.cancel()
}
```

## Context and Configuration Injection ✅

Kastrax allows you to inject context and configuration into tools, making them more flexible and adaptable to different scenarios:

```kotlin
import ai.kastrax.core.tool.Tool
import ai.kastrax.core.tool.ToolContext
import ai.kastrax.core.tool.ToolResult

class ConfigurableWeatherTool : Tool<WeatherToolInput, WeatherToolOutput> {
    // Tool definition omitted for brevity

    override suspend fun execute(
        input: WeatherToolInput,
        context: ToolContext = ToolContext()
    ): ToolResult<WeatherToolOutput> {
        // Get user preferences from context
        val temperatureUnit = context.getPreference("temperature_unit") ?: "celsius"
        val language = context.getPreference("language") ?: "en"

        // Get API configuration from context
        val apiKey = context.getConfig("weather_api_key")
            ?: return ToolResult.Error("Weather API key not configured")

        return try {
            // Use the context information in the API call
            val weatherData = fetchWeatherData(
                city = input.city,
                units = if (temperatureUnit == "celsius") "metric" else "imperial",
                language = language,
                apiKey = apiKey
            )

            ToolResult.Success(weatherData.toOutput())
        } catch (e: Exception) {
            ToolResult.Error("Failed to fetch weather data: ${e.message}")
        }
    }
}
```

When using the tool, you can provide the context:

```kotlin
// Create a tool context with user preferences and configuration
val toolContext = ToolContext().apply {
    // User preferences
    setPreference("temperature_unit", "fahrenheit")
    setPreference("language", "en-US")

    // System configuration
    setConfig("weather_api_key", "your-api-key-here")
}

// Use the context when generating a response
val response = agent.generate(
    input = "What's the weather in Tokyo?",
    sessionId = "user-456",
    conversationId = "weather-session",
    toolContext = toolContext
)
```

## Testing and Debugging Tools ✅

Kastrax provides robust support for testing and debugging tools. Here's how to write tests for your tools using Kotlin's testing frameworks:

```kotlin filename="src/test/kotlin/ai/kastrax/tools/WeatherToolTest.kt"
import ai.kastrax.core.tool.ToolResult
import ai.kastrax.tools.WeatherTool
import ai.kastrax.tools.WeatherToolInput
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue

class WeatherToolTest {
    private val weatherTool = WeatherTool()

    @Test
    fun `test successful weather retrieval`() = runBlocking {
        // Arrange
        val input = WeatherToolInput(city = "Tokyo", units = "metric")

        // Act
        val result = weatherTool.execute(input)

        // Assert
        assertTrue(result is ToolResult.Success)
        if (result is ToolResult.Success) {
            val output = result.value
            assertEquals("Sunny", output.conditions)
            // Add more assertions as needed
        }
    }

    @Test
    fun `test error handling for invalid city`() = runBlocking {
        // Arrange
        val input = WeatherToolInput(city = "", units = "metric")

        // Act
        val result = weatherTool.execute(input)

        // Assert
        assertTrue(result is ToolResult.Error)
        if (result is ToolResult.Error) {
            assertTrue(result.message.contains("city"))
        }
    }
}
```

## Using Tools in Practice ✅

Let's see a complete example of how to use a tool with an agent in a real application:

```kotlin filename="src/main/kotlin/ai/kastrax/examples/WeatherAssistantExample.kt"
import ai.kastrax.agents.WeatherAgent
import ai.kastrax.core.tool.ToolContext
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    // Create the weather agent
    val weatherAgent = WeatherAgent().agent

    // Create a tool context with necessary configuration
    val toolContext = ToolContext().apply {
        setConfig("weather_api_key", System.getenv("WEATHER_API_KEY") ?: "demo-key")
        setPreference("temperature_unit", "celsius")
    }

    // Generate a response using the agent and its tools
    val response = weatherAgent.generate(
        input = "What's the current weather in Paris, France?",
        sessionId = "user-789",
        conversationId = "weather-inquiry",
        toolContext = toolContext
    )

    // Print the response
    println("Agent response: ${response.text}")

    // The agent will automatically use the WeatherTool to fetch and include
    // current weather information for Paris in its response
}
```

The agent will use the `WeatherTool` to fetch the current weather in Paris and incorporate that information into its response.

## Integration with External Systems ✅

Kastrax tools can integrate with various external systems and APIs. Here's an example of a tool that integrates with a database:

```kotlin filename="src/main/kotlin/ai/kastrax/tools/DatabaseTool.kt"
import ai.kastrax.core.tool.Tool
import ai.kastrax.core.tool.ToolDefinition
import ai.kastrax.core.tool.ToolResult
import kotlinx.serialization.Serializable
import java.sql.Connection
import java.sql.DriverManager

@Serializable
data class QueryInput(
    val query: String,
    val limit: Int = 10
)

@Serializable
data class QueryResult(
    val columns: List<String>,
    val rows: List<List<String>>
)

class DatabaseTool : Tool<QueryInput, QueryResult> {
    override val definition = ToolDefinition(
        name = "database_query",
        description = "Execute a SQL query against the database to retrieve information",
        inputType = QueryInput::class,
        outputType = QueryResult::class
    )

    private var connection: Connection? = null

    private fun getConnection(context: ToolContext): Connection {
        if (connection == null || connection?.isClosed == true) {
            val jdbcUrl = context.getConfig("jdbc_url")
                ?: throw IllegalStateException("Database URL not configured")
            val username = context.getConfig("db_username") ?: ""
            val password = context.getConfig("db_password") ?: ""

            connection = DriverManager.getConnection(jdbcUrl, username, password)
        }
        return connection!!
    }

    override suspend fun execute(input: QueryInput, context: ToolContext): ToolResult<QueryResult> {
        return try {
            // Sanitize and validate the query for security
            val sanitizedQuery = sanitizeQuery(input.query, input.limit)

            // Execute the query
            val conn = getConnection(context)
            val statement = conn.createStatement()
            val resultSet = statement.executeQuery(sanitizedQuery)

            // Process the results
            val metadata = resultSet.metaData
            val columnCount = metadata.columnCount

            // Extract column names
            val columns = (1..columnCount).map { metadata.getColumnName(it) }

            // Extract rows
            val rows = mutableListOf<List<String>>()
            while (resultSet.next() && rows.size < input.limit) {
                val row = (1..columnCount).map {
                    resultSet.getString(it) ?: "null"
                }
                rows.add(row)
            }

            ToolResult.Success(QueryResult(columns, rows))
        } catch (e: Exception) {
            ToolResult.Error("Database query failed: ${e.message}")
        }
    }

    private fun sanitizeQuery(query: String, limit: Int): String {
        // Implement query sanitization and validation
        // This is a simplified example - in production, use prepared statements
        // and more sophisticated validation
        if (!query.lowercase().startsWith("select")) {
            throw IllegalArgumentException("Only SELECT queries are allowed")
        }

        return if (query.lowercase().contains(" limit ")) {
            query
        } else {
            "$query LIMIT $limit"
        }
    }
}
```

## Tool Design Best Practices ✅

When creating tools for your Kastrax agents, follow these guidelines to ensure reliable and intuitive tool usage:

### Tool Descriptions

Your tool's description should focus on its purpose and value:

- Keep descriptions simple and focused on **what** the tool does
- Emphasize the tool's primary use case
- Avoid implementation details in the main description
- Focus on helping the agent understand **when** to use the tool

```kotlin
ToolDefinition(
    name = "document_search",
    description = "Search the knowledge base to find information needed to answer user questions",
    inputType = SearchInput::class,
    outputType = SearchResult::class
)
```

### Input and Output Types

Design clear, well-structured input and output types:

- Make parameters self-documenting with clear names
- Include default values for optional parameters
- Use appropriate types for each parameter
- Document the purpose of each field

```kotlin
@Serializable
data class SearchInput(
    val query: String,  // The search query to find relevant information
    val limit: Int = 10,  // Number of results to return
    val filters: Map<String, String> = mapOf()  // Optional filters to apply
)
```

### Error Handling

Implement robust error handling in your tools:

- Return clear error messages that explain what went wrong
- Handle expected failure cases gracefully
- Provide guidance on how to fix the issue when possible
- Log detailed error information for debugging

### Security Considerations

Ensure your tools are secure:

- Validate and sanitize all inputs
- Use prepared statements for database queries
- Implement proper authentication and authorization
- Limit access to sensitive operations
- Don't expose sensitive information in error messages

## Advanced Tool Features ✅

Kastrax supports several advanced tool features:

### Tool Composition

You can compose multiple tools together to create more complex functionality:

```kotlin
class CompositeSearchTool(
    private val webSearchTool: WebSearchTool,
    private val databaseTool: DatabaseTool,
    private val documentTool: DocumentTool
) : Tool<CompositeSearchInput, CompositeSearchResult> {
    // Implementation that uses multiple tools together
}
```

### Tool Versioning

Implement versioning for your tools to maintain backward compatibility:

```kotlin
class WeatherToolV2 : Tool<WeatherToolInputV2, WeatherToolOutputV2> {
    override val definition = ToolDefinition(
        name = "get_weather_v2",
        description = "Enhanced weather information tool with additional data",
        inputType = WeatherToolInputV2::class,
        outputType = WeatherToolOutputV2::class,
        version = "2.0"
    )
    // Implementation
}
```

### Tool Metrics and Monitoring

Implement metrics collection for your tools:

```kotlin
class MonitoredTool<I : Any, O : Any>(private val delegate: Tool<I, O>) : Tool<I, O> {
    override val definition = delegate.definition

    override suspend fun execute(input: I, context: ToolContext): ToolResult<O> {
        val startTime = System.currentTimeMillis()
        try {
            val result = delegate.execute(input, context)
            val duration = System.currentTimeMillis() - startTime
            recordMetrics(definition.name, duration, result is ToolResult.Success)
            return result
        } catch (e: Exception) {
            val duration = System.currentTimeMillis() - startTime
            recordMetrics(definition.name, duration, false)
            throw e
        }
    }

    private fun recordMetrics(toolName: String, durationMs: Long, success: Boolean) {
        // Record metrics to your monitoring system
    }
}
```

## Integration with Actor Model ✅

One of Kastrax's unique features is the integration of tools with the actor model, allowing for distributed tool execution:

```kotlin
import ai.kastrax.actor.ActorRef
import ai.kastrax.actor.ActorSystem
import ai.kastrax.tools.WeatherTool

class ToolActorSystem(private val system: ActorSystem) {
    // Create actor references for tools
    val weatherToolActor: ActorRef = system.actorOf(WeatherToolActor.props(WeatherTool()))
    val databaseToolActor: ActorRef = system.actorOf(DatabaseToolActor.props(DatabaseTool()))

    // Tool actor message types
    data class ToolRequest<I>(val input: I, val replyTo: ActorRef)
    data class ToolResponse<O>(val result: ToolResult<O>)

    // Example of using a tool through the actor system
    suspend fun getWeather(city: String): ToolResult<WeatherToolOutput> {
        val input = WeatherToolInput(city = city)
        val response = system.ask<ToolResponse<WeatherToolOutput>>(
            weatherToolActor,
            ToolRequest(input, system.selfRef())
        )
        return response.result
    }
}
```

This integration enables building sophisticated multi-agent systems where tools can be distributed across different nodes and executed concurrently.
